package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.client.ESFeignClient;
import cn.itsource.ymcc.constant.*;
import cn.itsource.ymcc.domain.CourseDoc;
import cn.itsource.ymcc.domain.CourseOrder;
import cn.itsource.ymcc.domain.CourseOrderItem;
import cn.itsource.ymcc.domain.MessageSms;
import cn.itsource.ymcc.dto.PreOrderDto;
import cn.itsource.ymcc.mapper.CourseOrderItemMapper;
import cn.itsource.ymcc.mapper.CourseOrderMapper;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.service.ICourseOrderService;
import cn.itsource.ymcc.util.AssertUtil;
import cn.itsource.ymcc.util.CodeGenerateUtils;
import cn.itsource.ymcc.vo.CourseOrderVo;
import cn.itsource.ymcc.vo.KillCourseVo;
import cn.itsource.ymcc.vo.PayOrderVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zongchangjiang
 * @since 2023-04-10
 */
@Service
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private ESFeignClient esFeignClient;
    @Autowired
    private CourseOrderItemMapper courseOrderItemMapper;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public String placeOrder(CourseOrderVo courseOrderVo, Long userId) {
        AssertUtil.isTrue(redisTemplate.hasKey(courseOrderVo.getToken()),"请勿重复提交");
        //删除重复校验token
        redisTemplate.delete(courseOrderVo.getToken());
        CourseOrder courseOrder = null;

        try {
            //要不要判断有没有id
            AssertUtil.isNotNull(courseOrderVo.getCourseIds(),"非法请求，请重新购买");
            //我要保证两边都可以使用
            JsonResult courseDocByCourseIds = esFeignClient.findCourseDocByCourseIds(courseOrderVo.getCourseIds());
            //判断服务有没有调用成功
            AssertUtil.isTrue(courseDocByCourseIds.getSuccess(),courseDocByCourseIds.getMessage());
            //服务调用成功之后
            Object data = courseDocByCourseIds.getData();
            List<CourseDoc> CourseDocList = JSON.parseArray(JSON.toJSONString(data), CourseDoc.class);
            //保存订单信息
            courseOrder = courseDocToCourseOrder(courseOrderVo, CourseDocList, userId);
//            baseMapper.insert(courseOrder);
            //保存订单明细
            List<CourseOrderItem> items = saveOrderItem(courseOrder, CourseDocList);
            //因为我要保存事务消息  由于只能传入一个参数过去所以我进行一个封装
            courseOrder.setItems(items);

            sendMessage(courseOrder);

            sendTXMessage(userId, courseOrder, items);

            //开始发送延迟消息  作用 防止下单未支付
            /*
                主题 tags
                消息
                超时时间
                延迟等级
             */
            rocketMQTemplate.syncSend(
                    ConstantsOrderMq.TOPIC_COURSE_ORDER_DEALY + ":" + ConstantsOrderMq.TAGS_COURSE_ORDER_DEALY,
                    MessageBuilder.withPayload(courseOrder.getOrderNo()).build(),2000,2);
        } catch (Exception e) {
            //你要去处理   手动提交事务
            e.printStackTrace();
            redisTemplate.opsForValue().set(courseOrderVo.getToken(),"");
        }

        //返回订单编号
        return courseOrder.getOrderNo();
    }

    private void sendTXMessage(Long userId, CourseOrder courseOrder, List<CourseOrderItem> items) {
        /*
         * 发送事务消息   先保证本地事务  在去消费(支付单的保存)
         * String txProducerGroup,  生产者的名字
         * String destination,   topic +“:”+ tags
         * Message<?> message,   支付单的消息   支付单要些什么数据
         * Object arg            本店事务要的数据
         */
        PayOrderVo payOrder = new PayOrderVo();
        payOrder.setAmount(courseOrder.getPayAmount());
        payOrder.setPayType(courseOrder.getPayType());
        //payOrder.setRelationId(); //目前还没有
        payOrder.setOrderNo(courseOrder.getOrderNo());
        payOrder.setUserId(userId);

        //设置课程ID
        List<String> courseIds = items.stream().map(item -> item.getCourseId().toString())
                .collect(Collectors.toList());
        // ?courseIds=21,22,23
        payOrder.setExtParams("?courseIds="+String.join(",",courseIds)); //目前还没有
        payOrder.setSubject(courseOrder.getTitle());
        payOrder.setPayStatus(courseOrder.getStatusOrder());
        //开始执行事务消息
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(OrderConstants.MQ_COURSEORDER_PAY_GROUP_TRANSACTION,
                CourseConstants.MQ_TOPIC_ORDER + ":" + CourseConstants.MQ_TAGS_COURSEORDER_PAYORDER,
                MessageBuilder.withPayload(JSON.toJSONString(payOrder)).build(),
                courseOrder
        );
        AssertUtil.isTrue(result.getLocalTransactionState() == LocalTransactionState.COMMIT_MESSAGE,"事务消息发送失败");
    }

    private void sendMessage(CourseOrder courseOrder) {
        //通知用户
        //5.消息推送 为了钱 (如果有人关注了当前课程  我们是不是通知他  短信 邮箱 站内信 )  场景
        //必须使用异步 多线程  定时器  redis mq
        //事务消息  单向() 同步  异步
        //惊喜惊喜，期待已久的课程 java大神班 发布了,<a href='http://www.itsource.cn?courseId=7'>点我</a>快速尝鲜。
        //短信
        MessageSms messageSms = new MessageSms();
        messageSms.setContent("恭喜你下单成功，请尽快支付后观看:"+ courseOrder.getTitle());
        //查询数据库找当前课程关注人
        messageSms.setPhone("13388888888");
        messageSms.setUserId(-1L);
        messageSms.setTitle(courseOrder.getTitle());
        String sms = JSON.toJSONString(messageSms);
        rocketMQTemplate.sendOneWay(CourseConstants.TOPIC_COURSE_MESSAGE_SMS+":"+CourseConstants.TAGS_COURSE_MESSAGE_SMS,
                sms);
        //邮箱 发送
        //站内信
    }

    @Override
    public String killPlaceOrder(KillCourseVo killCourseVo,Long userId) {
        //要不要判断有没有id
        AssertUtil.isNotNull(killCourseVo.getOrderNo(),"非法请求，请重新购买");
        //我先要去取值
        PreOrderDto preOrderDto = (PreOrderDto) redisTemplate.opsForValue()
                .get(KillConstants.KEY_PREFIX_PRE_ORDER + ":" + killCourseVo.getOrderNo());
        AssertUtil.isNotNull(preOrderDto,"秒杀已过期");

        //保存订单信息
        CourseOrder courseOrder = preOrderDtoToCourseOrder(killCourseVo, preOrderDto, userId);
        CourseOrderItem items = saveOrderItem(courseOrder, preOrderDto);
        List<CourseOrderItem> itemList = Arrays.asList(items);
        courseOrder.setItems(itemList);
        //发送消息
        sendMessage(courseOrder);
        //发送事务消息
        sendTXMessage(userId, courseOrder, itemList);
        return killCourseVo.getOrderNo();
    }

    private CourseOrder preOrderDtoToCourseOrder(KillCourseVo killCourseVo,PreOrderDto preOrderDto,Long userId){
        CourseOrder courseOrder = new CourseOrder();
//        创建时间
        courseOrder.setCreateTime(new Date());
//        订单编号
        courseOrder.setOrderNo(preOrderDto.getOrderNo());
//        支付总的价格
        //只要是钱相关的都是使用BigDecimal 银行用的最多
        courseOrder.setTotalAmount(preOrderDto.getPrice());
        courseOrder.setPayAmount(preOrderDto.getPrice());
//        优惠金额
        courseOrder.setDiscountAmount(new BigDecimal(0.0));
//        数量
        courseOrder.setTotalCount(preOrderDto.getCount());
//        订单状态：0下单成功待支付，1已支付，2订单取消，3订单退款，4支付超时或失败，5订单确认订单完成
        courseOrder.setStatusOrder(ConstantsOrder.ORDER_STATUS_0);
//        userid用户
        courseOrder.setUserId(userId);
//        订单标题
        courseOrder.setTitle(preOrderDto.getTitle());
//        支付方式:0余额直接，1支付宝，2微信,3银联
        courseOrder.setPayType(killCourseVo.getPayType());
//        0普通订单 ， 1秒杀订单
        courseOrder.setOrderType(preOrderDto.getOrderType());
        return courseOrder;
    }

    private CourseOrderItem saveOrderItem(CourseOrder courseOrder,PreOrderDto preOrderDto){

            CourseOrderItem courseOrderItem = new CourseOrderItem();
//            对应订单ID
//            courseOrderItem.setOrderId(courseOrder.getId());
//            课程价格  原价
            courseOrderItem.setAmount(preOrderDto.getPrice());
//            小计金额
            courseOrderItem.setPrice(preOrderDto.getPrice());
//            课程的数量
            courseOrderItem.setCount(preOrderDto.getCount());
//            课程ID
            courseOrderItem.setCourseId(preOrderDto.getCourseId());
//            课程名字
            courseOrderItem.setCourseName(preOrderDto.getCourseName());
//            封面
            courseOrderItem.setCoursePic(preOrderDto.getCoursePic());
//            订单编号
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
//            秒杀课程ID
            courseOrderItem.setKillCourseId(preOrderDto.getKillCourseId());//todo
//            courseOrderItemMapper.insert(courseOrderItem);
        return courseOrderItem;
    }


    private CourseOrder courseDocToCourseOrder(CourseOrderVo courseOrderVo,List<CourseDoc> CourseDocList,Long userId){
        CourseOrder courseOrder = new CourseOrder();
//        创建时间
        courseOrder.setCreateTime(new Date());
//        订单编号
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(userId));
//        支付总的价格
        //计算总价
        List<Double> collect = CourseDocList.stream()
                .map(CourseDoc::getPriceOld).collect(Collectors.toList());//就是只从CourseDoc 对象里面 只取 PriceOld
        //只要是钱相关的都是使用BigDecimal 银行用的最多
        BigDecimal bigDecimal = new BigDecimal(0.0);
        for (Double aDouble : collect) {
            bigDecimal = bigDecimal.add(new BigDecimal(aDouble));
        }
        courseOrder.setTotalAmount(bigDecimal);
//        支付金额
        List<Double> collectPrice = CourseDocList.stream()
                .map(CourseDoc::getPrice).collect(Collectors.toList());
        BigDecimal bigDecimalPrice = new BigDecimal(0.0);
        for (Double aDouble : collectPrice) {
            bigDecimalPrice = bigDecimalPrice.add(new BigDecimal(aDouble));
        }
        courseOrder.setPayAmount(bigDecimalPrice);
//        优惠金额
        courseOrder.setDiscountAmount(bigDecimal.subtract(bigDecimalPrice));
//        数量
        courseOrder.setTotalCount(CourseDocList.size());
//        订单状态：0下单成功待支付，1已支付，2订单取消，3订单退款，4支付超时或失败，5订单确认订单完成
        courseOrder.setStatusOrder(ConstantsOrder.ORDER_STATUS_0);
//        userid用户
        courseOrder.setUserId(userId);
//        订单标题
        if(courseOrderVo.getType() == ConstantsOrder.ORDER_TYPE_0){
            courseOrder.setTitle(ConstantsOrder.ORDER_TYPE_0_TITLE);
        }else {
            courseOrder.setTitle(ConstantsOrder.ORDER_TYPE_1_TITLE);
        }
//        支付方式:0余额直接，1支付宝，2微信,3银联
        courseOrder.setPayType(courseOrderVo.getPayType());
//        0普通订单 ， 1秒杀订单
        courseOrder.setOrderType(courseOrderVo.getType());
        return courseOrder;
    }

    private List<CourseOrderItem> saveOrderItem(CourseOrder courseOrder,List<CourseDoc> CourseDocList){
        List<CourseOrderItem> items = new ArrayList<>();
        CourseDocList.forEach(doc->{
            CourseOrderItem courseOrderItem = new CourseOrderItem();
//            对应订单ID
//            courseOrderItem.setOrderId(courseOrder.getId());
//            课程价格  原价
            courseOrderItem.setAmount(new BigDecimal(doc.getPriceOld()));
//            小计金额
            courseOrderItem.setPrice(new BigDecimal(doc.getPrice()));
//            课程的数量
            courseOrderItem.setCount(RConst.Num.INT_1);
//            课程ID
            courseOrderItem.setCourseId(doc.getId());
//            课程名字
            courseOrderItem.setCourseName(doc.getName());
//            封面
            courseOrderItem.setCoursePic(doc.getPic());
//            订单编号
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
//            秒杀课程ID
//            courseOrderItem.setKillCourseId();//todo
//            courseOrderItemMapper.insert(courseOrderItem);
            items.add(courseOrderItem);
        });
        return items;
    }

    @Override
    @Transactional
    public boolean save(CourseOrder courseOrder) {
        super.save(courseOrder);
        List<CourseOrderItem> items = courseOrder.getItems();
        items.forEach(item ->{
            item.setOrderId(courseOrder.getId());
            courseOrderItemMapper.insert(item);
        });
        return true;
    }
}
